前面兩天講的都是「它為什麼會錯」。
Day 9 說錯誤發生在接線,不在零件。Day 10 說那是因為資料缺了一個維度。
今天要講一件更難堪的事:
那個維度之所以缺,不是因為誰漏寫了一欄。是因為我從一開始,就拿錯了那份資料。
一句話就講得完:
我把票卡餵給 AI,要它產出一份「這個系統現在長什麼樣子」的說明書。
現在寫出來看起來很蠢,但我想誠實說明為什麼當時覺得這是對的——因為那個理由到今天都還很有說服力。
第一,卡片是現成的。 一百多張,已經在那裡了,不需要任何人再寫一次。
第二,卡片是文字。 它是 AI 最擅長處理的東西:自然語言、有上下文、有前因後果。
第三,卡片裡有「為什麼」。 程式碼只會告訴你現在是什麼樣子,不會告訴你為什麼當初這樣決定。而那些理由,全部躺在卡片的描述和留言裡。
第四,那是唯一有人在維護的地方。 沒有人在寫規格文件,但所有人都會開卡。
四個理由都是真的。我到現在都不覺得它們錯。
錯的是我從這四點推出來的結論。
我後來花了一點時間,才把這件事想清楚。
票卡不是現況的描述,它是意圖的紀錄。
而且是一種很特別的意圖紀錄——會互相覆蓋的。
一張卡開出來的時候,它說的是「我希望它變成這樣」。這句話在那個時間點是有效的。但三個月後,同一個地方被另一張卡改掉了,第一張卡那句話沒有被刪掉,也沒有被標記過期——它就靜靜地躺在那裡,長得跟昨天剛開的卡一模一樣。
我想了一個比喻:
票卡是航海日誌,不是地圖。
日誌上的每一行都是真的:某天往東航行、某天遇到暴風、某天改道往北。
但你沒有辦法從日誌上讀出「現在船在哪裡」——除非你把每一行都按順序讀完,而且每一行都正確理解了它對前一行的修改。
而我做的事情,就是把一整本航海日誌交出去,然後說:「幫我畫一張地圖。」
把這個任務拆開來看,它不是一件事,是三件:
第一件:排序。
哪一張卡在前、哪一張在後?時間戳是有的,但時間戳只告訴你「開卡時間」。一張三個月前開的卡,可能上週才被實作;一張上週開的卡,可能還沒動工。「寫下來的順序」不等於「生效的順序」。
第二件:覆蓋判斷。
就算順序對了,你還得知道後面那張卡跟前面那張是什麼關係——是推翻、是補充、還是根本在講不同的地方?這個判斷需要理解兩張卡實際改動的範圍有沒有重疊。而那個範圍,卡片上通常不寫。
第三件:頁面歸屬。
就是 Day 10 講的那個缺掉的維度——這件事到底發生在哪一頁。
三件事,沒有一件的答案在那批資料裡。
而我當時的作法是:把三件事一起丟出去,要一個乾淨的結果。
它給了我一份乾淨的結果。那份結果裡有 8 處錯。
我到現在都覺得,那 8 處其實算少的。
而真正該問的,其實是一個很簡單的問題:
「這個功能現在長什麼樣子」——這個答案,到底住在哪裡?
不在卡片裡。卡片講的是「我們想讓它變成什麼樣子」,而且是好幾個時間點的版本疊在一起。
答案住在兩個地方:
這兩個東西有一個共同的性質,而這個性質正是卡片缺的:
它們是可觀測的。它們只有一個版本,就是現在這個。
程式碼不會同時存在三種寫法讓你猜哪個是現行的。你 checkout 下來,看到什麼就是什麼。
所以我把整件事翻過來:
| 原本 | 修正後 | |
|---|---|---|
| 「現在長什麼樣」 | 從卡片推論 | 從 code / 畫面直接讀 |
| 「為什麼是這樣」 | 混在同一份輸出裡 | 從卡片查,而且只查這個 |
這不是把卡片丟掉。卡片還是不可取代的——因為「為什麼當初決定不做另一個方案」這種事,程式碼裡一個字都沒有。Day 3 我寫過,那些被否決的決定是最容易消失的知識。
差別在於分工:
可觀測的事實,去可觀測的地方拿。
不可觀測的理由,才去卡片裡挖。
我不想把「以 code 為準」講得太漂亮,因為它有一個很硬的限制,而我是撞上去之後才知道的。
程式碼會告訴你現在是什麼樣子,但它不會告訴你「為什麼還沒做完」。
這個網站有些功能是沒有做完善的。有些地方看起來像 bug,其實是刻意留的缺口;有些欄位空著,是因為在等另一個系統;有些流程只做了一半,是因為當初的決定就是先做一半。
而這些事情,程式碼上通常不會有註解。你讀到的就是一段「看起來沒寫完」的實作,而它沒有任何地方告訴你:這是還沒做,還是不打算做,還是做不了。
所以完整的圖其實是三層,不是兩層:
| 你想知道的 | 去哪裡拿 |
|---|---|
| 現在長什麼樣 | code / 畫面——可觀測,只有一個版本 |
| 為什麼當初這樣決定 | 卡片——理由與否決紀錄 |
| 為什麼這裡還沒做完 | 常常兩邊都沒有 |
第三行是最難的,而且它沒有便宜的解法。
我後來的處理方式很土:遇到這種地方,就標記起來,不猜。 寧可在文件上留一句「這一段目前的狀態需要確認」,也不要讓 AI 去填一個合理的解釋——因為那正是 Day 10 講的,它會把空格填滿。
一個誠實的「不知道」,比一個通順的猜測有價值太多了。
這是我後來覺得整個專案最有價值的一個副作用。
當你把兩邊分開抓,一定會遇到對不上的情況:卡片說它應該是 A,但畫面上它是 B。
我一開始把這當成雜訊,想辦法要消掉它。
後來才發現:那才是我真正該交出去的東西。
那份清單上的每一條,都是一個「規格與實作已經分岔」的位置。而它們的成因不只一種:
最後那一條特別值得停一下。Day 9 那個退單的案例,收尾是「回頭修改那張票卡的敘述」——那就是一條規格漂移,而且方向跟直覺相反:錯的是規格,不是實作。
我想強調一件事:
這份清單不是 bug list。
Bug list 的預設是「實作錯了,去修」。但這份清單上有一半的條目,正確的處理方式是去修卡片,不是去修程式。
所以我後來叫它規格漂移清單——它只回答一個問題:「這兩邊在哪裡對不上」,至於哪一邊該改,那是下一步的判斷,不是它的工作。
而這個東西,原本的做法永遠產不出來。因為當你只用卡片當唯一來源,你連「對不上」這件事都看不見——你沒有第二個來源可以對。
寫到這裡,我必須承認一件有點好笑的事。
Day 7 和 Day 8 我花了兩篇在講,我怎麼驗證那 18 張統整卡:把每一條宣稱拆出來,去程式碼裡比對,判準二元,每一項都要附證據。
那就是我上面講的方法。
一模一樣:以 code 為準、逐條比對、對不上的列出來。
我當時以為我在做「品管」——一個產出之後的檢查步驟。
我沒有意識到,我手上那個檢查方法,其實就是正確的生產方法。
換句話說:我花了很大的力氣,建了一條產線,做出一批有瑕疵的成品;然後又花了很大的力氣,建了一套檢驗流程去抓瑕疵。
而那套檢驗流程如果一開始就拿來當產線,那批瑕疵根本不會產生。
我後來想過為什麼會這樣。我的結論是:
驗證和生產,在我腦袋裡本來是兩件事。
生產是「從無到有」,我直覺覺得那需要創造力,所以交給 AI;驗證是「對答案」,我覺得那是苦工,所以我盯著。
但在這個任務上,「從無到有」根本是個假象——答案已經在程式碼裡了,沒有人需要創造它。需要的只是把它讀出來、放對位置。
那從頭到尾就是一件對答案的工作。
我原本想把這一段寫成一個頓悟的故事。但我回頭看,發現事實比那個無聊,也比那個誠實。
因為有兩件事從頭到尾同時成立,而且從來沒有和解過:
我一開始就希望 AI 可以把所有事情都包下來。
而我一開始就不信任它。
所以最早的版本其實是反過來的——AI 負責整理,而檢查是我自己做的。
不是因為我想得多深,單純就是不信任而已。
因為不放心,我發展出一個習慣:同一件事,用不同的方式問它三次。
舉個具體的例子。假設它跟我說:
「這張卡上的 A 頁面有一個新功能,所以 B 頁面也有這個功能。」
我不會接受這個答案。我會反問回去:
- 你幫我查查,B 頁面有沒有自己的卡片?
- A 那張票卡,有沒有寫明說 B 頁面要有這個功能?
- 你是怎麼認定的?
- 這是最新的資訊嗎?
- 有沒有其他票卡有附註?
當時我以為我在做的事情是「防止它唬爛我」。
但現在回頭看,這五個問題全部在問同一件事:
你這個說法,是從哪裡來的?而那個來源,是不是現在的?
那就是 source of truth。我只是不知道我在做這件事。
我以為我在防守。實際上我在做的,是逼它把結論和來源分開放——而一旦分開放,哪些結論站得住、哪些站不住,自己就浮出來了。
這也解釋了上一節那個諷刺:我手上一直有正確的方法,只是我把它當成防禦手段在用,沒有意識到它可以當成建構方法。
而事情真正翻船,是在順序被打亂的時候。
新人的那批 error 回報進來,我被其中一條帶偏了。那一次,我沒有自己去查,我請 AI 去查。
Day 9 那條鏈的中段就是這件事——它給了我一個支持錯誤方向的答案,然後我開了一張不該開的單。
現在把兩件事並排,那個好笑的地方就出來了:
| 我怎麼做的 | 結果 | |
|---|---|---|
| 平常 | 不信任 → 自己檢查 → 追問到底 | 沒出事 |
| 那一次 | 被帶偏 → 交給它查 → 沒有追問 | 退單、RD 白做工 |
我唯一一次把查證交出去,就是唯一一次出事。
但我想很小心地講清楚一件事:這不是「AI 不可信」的故事。
真正的問題不在它身上,在於我那次帶著一個已經有答案的問題去問。我沒有問「B 頁面到底有沒有這個功能」,我問的是「是不是少了前期數字」——問題裡就已經藏好了我要的答案。
那五個追問之所以有效,不是因為它們很聰明,是因為它們逼的是來源,不是結論。 而我那次問的,是結論。
最後講一件很小、但我想留在這裡的事。
這樣問久了,我心裡其實有一點過意不去——我很怕一直這樣追問下去,會打擊到它的自信心。
我知道這句話不精確。它沒有自信心可以被打擊,那只是我的投射。
但那個感覺是真的,而且我覺得它透露了一件事:我已經在用對待同事的方式對待它了。 這件事後面還會再出現一次(Day 14)。
而感覺歸感覺,做法沒有變過。為了確保每一條都是對的,我只能繼續這樣問。
我後來對這件事的結論是:追問不是不信任的表現,它是把工作交出去的前提。
選對來源之後,下一個問題馬上就來了。
如果事實要從 code 和畫面抓,那意味著我得讓 AI 接觸到更多東西——更多程式碼、更多環境、更多實際資料。
而這裡有一條線我不打算跨過去。
有些資料,我看過之後的結論是:這個不能餵。
明天那篇要講的是,那條線畫在哪裡,以及我是怎麼決定的。